Repository navigation
Q23: RL vehicle preset input, RL guide, demo videos - #15
Merged
Merged
Conversation
EnvConfig gains vehicle/tire preset stems. load_vehicle_preset() reads
configs/vehicles/<stem>.yaml and configs/parts/tire/<stem>.yaml once in
the parent process and writes no file. Keys the C++ parser would ignore,
missing required keys, and catalog-format tyre parts raise ValueError
instead of silently falling back to the C++ generic defaults.
vehicle=None keeps the built-in car and warns. reset() info and env
metadata carry vehicle, tire and a sha256 of the effective parameters.
configs/rl/{default,fast}_env.yaml select ioniq5_awd + ioniq5_pac2002.
tests/rl/test_rl_env.py: Q23-1..6 (plant weight from tyre Fz matches the
YAML mass, DR scales the Ioniq5 baseline, no file writes, strict keys);
paths made repo-relative so the script runs from any worktree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs/RL_GUIDE.md: background, concepts (vehicle preset, obs fields with units/frame, action, reward, termination, reset and domain randomization, seeds), quick start, three examples (random 16 envs, SB3 PPO, friction DR), the two videos, and limits (throughput; the Ioniq 5 preset measured at ~2.7x fewer ticks/s than the generic car; G1 substep gate not repeated with the preset; preset is a public approximation). examples/rl/record_vec_demo.py records a VDSimVecEnv rollout (plant state per control step, terminal state kept before reset) to npz, or one .vdtrace per env. examples/rl/render_vec_grid.py draws the gym tile grid or the one-road overlay and encodes with system ffmpeg. Frames are drawn only at recorded steps. vdsim_render's API is unchanged. docs/assets/rl: vec_grid.mp4 (16 envs, random policy, 20 s) and vec_overlay.mp4 (64 envs, shared lane-keep + disturbance, 15 s), with start / reset / end stills. mkdocs nav gains the guide. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…st rl_env (Q23-d, Q23-e) Q23-d - tests/rl/gate_g1_substep_accuracy.py takes --vehicle/--tire/--level/--out and records the preset param_hash; default (no preset, L3) is unchanged. - configs/rl/fast_env.yaml: the 2026-09-17 table is labelled as the C++ built-in car at L3; a second table measures the car this file selects (ioniq5_awd + ioniq5_pac2002, param_hash 04652687..., L2). At 2.5 ms, ay 0.27 % and yaw 0.12 % pass, Fz_FL 7.56 % fails the 5 % criterion. max_substep_dt is NOT changed (pending PO decision). - tests/rl/bench_preset_cost.py: one-factor-at-a-time core throughput. The whole ~2.7x gap is the MF2002 tyre backend; vehicle params and plant_path move throughput by < 6 %. Measurement only, nothing tuned. - examples/rl/record_vec_demo.py: per-env disturbance generators (SeedSequence(seed).spawn(n)) unless --shared-actions; commit stamp gets -dirty when the tree differs from HEAD; caption carries a vehicle note (default "public approximation"). - docs/RL_GUIDE.md: video B text/command and Limits updated. Q23-e - ctest target rl_env runs tests/rl/test_rl_env.py (gymnasium, no torch). The SB3 PPO smoke moved to tests/rl/smoke_sb3_ppo.py, outside ctest. - build.yml installs gymnasium==0.29.1 in both jobs; a missing gymnasium fails rl_env (ModuleNotFoundError), it is not skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The one-line provenance caption overflowed the 1280 px overlay frame and cut off the commit stamp. Split after level=. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Recorded and rendered from a clean tree at 8135a76, which contains both scripts; captions carry commit=8135a76 and "(public approximation)". Video B now uses per-env disturbance generators: 26 off-track exits split 16 at y=+6 m / 10 at y=-6 m (was one-sided with a shared draw). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Ioniq 5 preset fails the G1 Fz_FL <= 5 % criterion at 2.5 ms (7.56 %). Of the two passing combinations, take the generic car at 2.5 ms (L2: ay 1.45 %, yaw 0.53 %, Fz_FL 4.07 %, 326 k steps/s) over Ioniq 5 at 1.0 ms (Fz_FL 2.62 %, 51 k steps/s), which is already default_env.yaml. Criterion unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PO decision 78 / 18_dev_briefing_0903 section 42: the shipped RL declarations
must not name a car whose parameters are not published.
- configs/vehicles/generic_sedan.yaml + configs/parts/tire/generic_pacejka.yaml:
the values core/src/params.cpp compiles in as VehicleParams()/TireParams(),
written out as files. generic_pacejka is a flat TireParams file because
vdsim_rl refuses catalog parts (schema/body) by design, and
configs/parts/tire/default_pacejka.yaml is a catalog part.
- configs/rl/{default_env,fast_env}.yaml select that pair. Same plant as
before (fast_env ran the unnamed built-in car), so the G1 substep table in
fast_env.yaml still describes both files; default_env's 1 ms row is
ay 0.55 %, yaw 0.20 %, Fz_FL 1.22 %.
- load_vehicle_preset resolves a preset as vehicle_file/tire_file (absolute
path) -> $VDSIM_PRIVATE_CONFIGS/<layout> -> configs/<layout>, so a car that
cannot be published stays outside the repository. A relative
vehicle_file/tire_file is refused (it would depend on the working directory).
- provenance/info/metadata gain source=public|private and still carry names
only, never a path.
- wheels.yml CIBW smoke builds the generic pair instead of the Ioniq 5 pair.
- tests/rl/test_rl_env.py: Q23-f1 compares all 79 bound fields of the loaded
params against the C++ defaults and asserts the same param_hash as
vehicle=None, so a drift between YAML and C++ fails instead of silently
training a different car; Q23-f2 walks the search order and the refusal of a
relative path. No new ctest target (505 registered, unchanged).
- docs/RL_GUIDE.md: generic defaults, the private-root recipe, source in info.
The Ioniq 5 bullet no longer claims its numbers are a public approximation --
that was a self-reported provenance, not a measured one.
- docs/VALIDATION.md currency -> PENDING: this tree registers rl_env on top of
main, so the canonical count is what CI measures three times on the Q23 PR.
Local: validation preset 505/505, 0 failed, twice in a row.
Negative check: mass 1500 -> 1400 in generic_sedan.yaml fails Q23-f1 with the
field named; dropping the private root from the search order fails Q23-f2.
The branch added 37 lines matching /ioniq/i on top of origin/main, which would enlarge what Q24 has to clean up. Judgement criterion, fixed before the edit: git diff origin/main...HEAD | grep '^+' | grep -v '^+++' | grep -ic ioniq == 0. - configs/rl/fast_env.yaml: drop the Ioniq 5 G1 table (b) and the two-candidate narrative. The generic-car tables (a) and the fixed criterion stay; a new paragraph says a substep is a property of the car and that non-public presets are injected through $VDSIM_PRIVATE_CONFIGS with their tables kept alongside them. - docs/RL_GUIDE.md: §6 now carries the generic 1.0/2.5 ms rows only, plus one bullet stating that a preset reading a measured .tir through MF2002 has measured ~2.7x slower with a larger Fz error (no car named), and one bullet documenting the private-injection search order. - tests/rl/test_rl_env.py: Q23-1/4/6 used ioniq5_awd.yaml as the fixture. Every shipped car carries the C++ default values, so the same checks against one of them would be vacuous; they now write a synthetic 1750 kg probe car into a temporary $VDSIM_PRIVATE_CONFIGS root, the same mechanism Q23-f2 already exercises. Q23-1 asserts the fixture actually differs from the shipped values, so a future edit cannot make the check vacuous silently. The root is scoped to the blocks that use it: left set, it also resolves the shipped tyre privately and flips info[source] for the public declarations Q23-2 checks. - tests/rl/gate_g1_substep_accuracy.py: docstring example uses the generic stems and points at the private root for anything else. - tests/rl/bench_preset_cost.py: removed. It exists only to explain this one preset's cost; moved to the private path with the table it feeds. - examples/rl/record_vec_demo.py: --vehicle-note no longer defaults to a self-reported provenance string. Test count unchanged (no ctest target added or removed). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The shipped mp4s were recorded at 8135a76, a tree that did not contain examples/rl/record_vec_demo.py or render_vec_grid.py, so their caption pointed at a commit the commands could not be run from. They also captioned the car with a self-reported provenance string. Re-recorded from 41f1d99 with a clean work tree, so the caption reads commit=41f1d99 with no -dirty suffix and no provenance note, and the two commands in docs/RL_GUIDE.md section 5 reproduce these files at that commit. - vec_grid.mp4 20.04 s, 1280x820, 16 envs, random per-env policy, seed 0 - vec_overlay.mp4 15.04 s, 1280x460, 64 envs, scripted lane-keeper, seed 3 PNG frames are taken from the same render pass, not re-rendered separately: vec_grid_start (t=0), vec_grid_reset (t=1.16 s, env 07 shows "reset <- off_track"), vec_grid_end (t=20 s), vec_overlay_end (t=15 s). Per-env disturbance is unchanged and still spreads both ways: of the 47 envs whose first episode ends, 30 leave the road on the +y side and 17 on -y (28 off_track, 14 spin_out, 5 stall); final lateral positions across all 64 envs have a 2.28 m standard deviation and the longitudinal spread is 53 m. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gate_g1_substep_accuracy.py: --ref-substep (default 1 us), --max-substeps (default 6000; refuses to start below DT/min step), clamp count reset and checked after the run (non-zero => discarded, exit 3), grid 0.01..5 ms, effective h from ceil(DT/h), adjacent-pair order p, every row also compared against the 0.1 ms run so the old table is reproduced alongside. Result (generic car, L2): p = 1.01-1.06 from 1 ms to 0.01 ms, monotone. 0.1 ms reference self-error: ay 0.059 %, Fz_FL 0.117 %. 2.5 ms row moves 1.45 -> 1.51 % (ay) and 4.07 -> 4.19 % (Fz_FL): still passes. Old rows kept. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # docs/VALIDATION.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scope
RL environment vehicle preset input, RL usage guide, and demo videos (Q23-a/-b/-c),
plus the follow-up rounds Q23-f (public/private config split), Q23-g (public-tree
hygiene), and Q23-h (G1 reference solution re-integrated at 1 us).
mainwas merged into this branch (merge commit, no rebase — decision 98), so theasset caption sha
41f1d99stays an ancestor of this history and the videos did notneed re-recording.
Q23-a — vehicle/tire preset input
EnvConfig.vehicle/EnvConfig.tire: YAML stems, loaded once in the parentprocess, zero file writes (the Q10-c shared-resolve-cache race cannot recur here).
vehicle_fileabsolute path ->$VDSIM_PRIVATE_CONFIGS/vehicles/<stem>.yaml-> repository
configs/vehicles/<stem>.yaml. Relative paths are rejected.vehicle=Nonekeeps the C++ built-in parameters but emits a one-shotUserWarning.info(on reset) andenv.metadatacarryvehicle,tire,param_hashandsource=public|private— names only, never absolute paths.Q23-f — public defaults are generic, Ioniq5 is injected privately
configs/vehicles/generic_sedan.yamlandconfigs/parts/tire/generic_pacejka.yamltranscribe the C++
VehicleParams()/TireParams()defaults. A regression pins all79 bound fields plus
param_hashagainst the C++ defaults, so the YAML cannot drift.configs/rl/default_env.yamlandfast_env.yamlnow name the generic pair.wheels.ymlCIBW smoke uses the generic pair.default_pacejka; that file is a catalogue part and loading it wouldneed the catalogue parser this work order forbids, so the same tyre was transcribed
into flat form as
generic_pacejka.Q23-g — public-tree hygiene
git diff origin/main...HEAD | grep '^+' | grep -v '^+++' | grep -ic ioniq= 0.live outside the repository; the public tests use a synthetic 1750 kg probe car and
assert that the probe actually discriminates against the generic values (so the
fixture cannot become vacuous).
Q23-h — G1 reference solution
tests/rl/gate_g1_substep_accuracy.pynow integrates the reference at 1 us(was 0.1 ms), raises
max_substepsto 6000, and discards any run wheresolver_substep_clamp_count() != 0.sound. The old 0.1 ms reference carried 0.059 % (a_y) / 0.117 % (Fz_FL) of its own
discretisation error, so the earlier tables were optimistic by 3..11 % relative.
configs/rl/fast_env.yamlanddocs/RL_GUIDE.md§6; no threshold was relaxed andno metric was dropped.
fast_env vehicle choice (decision 69/83)
fast_envuses the C++ built-in vehicle at L2 with a 2.5 ms max substep: a_y 1.45 %,yaw 0.53 %, Fz_FL 4.19 % against the 1 us reference — inside the pre-registered
2 % / 5 % gate, at 6.4x the throughput of the alternative. The Ioniq5 combination
fails that gate at 2.5 ms (Fz_FL 7.83 %) and is therefore not used here.
Registration surface
cosim/CMakeLists.txtregisters one new ctest target,rl_env(torch/SB3-free checksonly; the PPO smoke stays outside CI, and
gymnasiumis pinned in the workflow so animport failure is a FAIL, never a skip).
origin/main:cosim/CMakeLists.txtonly, +10 lines.number, the cause is to be investigated before any document is edited.
docs/VALIDATION.mdis therefore held atPENDING; it will be promoted only afterthree consecutive identical CI measurements on a fixed head.
Known, out of scope
test_part_store.pycan fail under highctest -jparallelism because the partcatalogue is written to a shared temporary path. The canonical measurement is serial,
so this does not affect the count; it is filed as a separate item.